home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / postgres / postgre4.z / postgre4 / src / utils / adt / nabstime.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-05-06  |  23.3 KB  |  1,012 lines

  1. /*
  2.  * getabsdate - parse almost any absolute date getdate(3) can (& some it can't)
  3.  *
  4.  * NOTE:  In porting this to postgres I condensed 6 files to 3.  So the abstime
  5.  *      adt is made up of the files nabstime.c dateconv.c and datetok.c
  6.  *      -mer 6 Feb 1992
  7.  *
  8.  * $Header: /private/postgres/src/utils/adt/RCS/nabstime.c,v 1.4 1992/02/28 05:34:42 mao Exp $
  9.  */
  10.  
  11. #include <stdio.h>
  12. #include <ctype.h>
  13. #include <string.h>
  14. #include <time.h>
  15. #include <sys/types.h>
  16. #include <sys/timeb.h>
  17. #include "tmp/postgres.h"
  18. #include "utils/nabstime.h"
  19.  
  20. #define MAXDATEFIELDS 25
  21.  
  22. #define ISSPACE(c) ((c) == ' ' || (c) == '\n' || (c) == '\t')
  23.  
  24. /* this is fast but dirty.  note the return's in the middle. */
  25. #define GOBBLE_NUM(cp, c, x, ip) \
  26.     (c) = *(cp)++; \
  27.     if ((c) < '0' || (c) > '9') \
  28.         return -1;        /* missing digit */ \
  29.     (x) = (c) - '0'; \
  30.     (c) = *(cp)++; \
  31.     if ((c) >= '0' && (c) <= '9') { \
  32.         (x) = 10*(x) + (c) - '0'; \
  33.         (c) = *(cp)++; \
  34.     } \
  35.     if ((c) != ':' && (c) != '\0' && !ISSPACE(c)) \
  36.         return -1;        /* missing colon */ \
  37.     *(ip) = (x)            /* N.B.: no semi-colon here */
  38.  
  39. #define EPOCH 1970
  40. #define DAYS_PER_400YRS    (time_t)146097
  41. #define DAYS_PER_4YRS    (time_t)1461
  42. #define SECS_PER_DAY    86400
  43. #define SECS_PER_HOUR    3600
  44. #define DIVBY4(n) ((n) >> 2)
  45. #define YRNUM(c, y) (DIVBY4(DAYS_PER_400YRS*(c)) + DIVBY4(DAYS_PER_4YRS*(y)))
  46. #define DAYNUM(c,y,mon,d)    (YRNUM((c), (y)) + mdays[mon] + (d))
  47. #define EPOCH_DAYNUM    DAYNUM(19, 69, 10, 1)    /* really January 1, 1970 */
  48.  
  49.  
  50. /* definitions for squeezing values into "value" */
  51. #define ABS_SIGNBIT 0200
  52. #define VALMASK 0177
  53. #define NEG(n)        ((n)|ABS_SIGNBIT)
  54. #define SIGNEDCHAR(c)    ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
  55. #define FROMVAL(tp)    (-SIGNEDCHAR((tp)->value) * 10)    /* uncompress */
  56. #define TOVAL(tp, v)    ((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10))
  57. #define IsLeapYear(yr) ((yr%4) == 0)
  58.  
  59. char nmdays[] = {
  60.     0, 31, 28, 31,  30, 31, 30,  31, 31, 30,  31, 30, 31
  61. };
  62. /* days since start of year. mdays[0] is March, mdays[11] is February */
  63. static short mdays[] = {
  64.     0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337
  65. };
  66.  
  67. /* exports */
  68. extern int dtok_numparsed;
  69.  
  70. /*
  71.  * parse and convert absolute date in timestr (the normal interface)
  72.  *
  73.  * Returns the number of seconds since epoch (January 1 1970 GMT)
  74.  */
  75. AbsoluteTime
  76. nabstimein(timestr)
  77. char *timestr;
  78. {
  79.     int tz = 0;
  80.     struct tm date;
  81.  
  82.     if (IsEpochStr(timestr))
  83.     return EPOCH_ABSTIME;
  84.     if (IsNowStr(timestr))
  85.     return GetCurrentTransactionStartTime();
  86.  
  87.     return (prsabsdate(timestr, NULL, &date, &tz) < 0) ?
  88.         INVALID_ABSTIME :
  89.         dateconv(&date, tz);
  90. }
  91.  
  92. int
  93. IsNowStr(tstr)
  94.     char *tstr;
  95. {
  96.     while (ISSPACE(*tstr))
  97.     tstr++;
  98.  
  99.     return ((tstr[0] == 'n' || tstr[0] == 'N') &&
  100.         (tstr[1] == 'o' || tstr[1] == 'O') &&
  101.         (tstr[2] == 'w' || tstr[2] == 'W')); 
  102. }
  103.  
  104.  
  105. int
  106. IsEpochStr(tstr)
  107.     char *tstr;
  108. {
  109.     while (ISSPACE(*tstr))
  110.     tstr++;
  111.     return ((tstr[0] == 'e' || tstr[0] == 'E') &&
  112.         (tstr[1] == 'p' || tstr[1] == 'P') &&
  113.         (tstr[2] == 'o' || tstr[2] == 'O') &&
  114.         (tstr[3] == 'c' || tstr[3] == 'C') &&
  115.         (tstr[4] == 'h' || tstr[4] == 'H'));
  116. }
  117.  
  118. /*
  119.  * just parse the absolute date in timestr and get back a broken-out date.
  120.  */
  121. int
  122. prsabsdate(timestr, now, tm, tzp)
  123. char *timestr;
  124. struct timeb *now;
  125. register struct tm *tm;
  126. int *tzp;
  127. {
  128.     register int nf;
  129.     char *fields[MAXDATEFIELDS];
  130.     static char delims[] = "- \t\n/,";
  131.  
  132.     nf = split(timestr, fields, MAXDATEFIELDS, delims+1);
  133.     if (nf > MAXDATEFIELDS)
  134.         return -1;
  135.     if (tryabsdate(fields, nf, now, tm, tzp) < 0) {
  136.         register char *p = timestr;
  137.  
  138.         /*
  139.          * could be a DEC-date; glue it all back together, split it
  140.          * with dash as a delimiter and try again.  Yes, this is a
  141.          * hack, but so are DEC-dates.
  142.          */
  143.         while (--nf > 0) {
  144.             while (*p++ != '\0')
  145.                 ;
  146.             p[-1] = ' ';
  147.         }
  148.         nf = split(timestr, fields, MAXDATEFIELDS, delims);
  149.         if (nf > MAXDATEFIELDS)
  150.             return -1;
  151.         if (tryabsdate(fields, nf, now, tm, tzp) < 0)
  152.             return -1;
  153.     }
  154.     return 0;
  155. }
  156.  
  157. /*
  158.  * try to parse pre-split timestr as an absolute date
  159.  */
  160. int
  161. tryabsdate(fields, nf, now, tm, tzp)
  162. char *fields[];
  163. int nf;
  164. struct timeb *now;
  165. register struct tm *tm;
  166. int *tzp;
  167. {
  168.     register int i;
  169.     register datetkn *tp;
  170.     register long flg = 0, ty;
  171.     int mer = HR24, bigval = -1;
  172.     struct timeb ftz;
  173.  
  174.     if (now == NULL) {        /* default to local time (zone) */
  175.         now = &ftz;
  176.         (void) ftime(now);
  177.     }
  178.     *tzp = now->timezone;
  179.  
  180.     tm->tm_mday = tm->tm_mon = tm->tm_year = -1;    /* mandatory */
  181.     tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
  182.     tm->tm_isdst = 0;
  183.     dtok_numparsed = 0;
  184.  
  185.     for (i = 0; i < nf; i++) {
  186.         if (fields[i][0] == '\0')
  187.             continue;
  188.         tp = datetoktype(fields[i], &bigval);
  189.         ty = (1L << tp->type) & ~(1L << IGNORE);
  190.         if (flg&ty)
  191.             return -1;        /* repeated type */
  192.         flg |= ty;
  193.         switch (tp->type) {
  194.         case YEAR:
  195.             tm->tm_year = bigval;
  196.             break;
  197.         case DAY:
  198.             tm->tm_mday = bigval;
  199.             break;
  200.         case MONTH:
  201.             tm->tm_mon = tp->value;
  202.             break;
  203.         case TIME:
  204.             if (parsetime(fields[i], tm) < 0)
  205.                 return -1;
  206.             break;
  207.         case DTZ:
  208. #if 0
  209.             tm->tm_isdst++;
  210. #endif
  211.             /* FALLTHROUGH */
  212.         case TZ:
  213.             *tzp = FROMVAL(tp);
  214.             break;
  215.         case IGNORE:
  216.             break;
  217.         case AMPM:
  218.             mer = tp->value;
  219.             break;
  220.         default:
  221.             return -1;    /* bad token type: CANTHAPPEN */
  222.         }
  223.     }
  224.     if (tm->tm_year == -1 || tm->tm_mon == -1 || tm->tm_mday == -1)
  225.         return -1;        /* missing component */
  226.     if (mer == PM)
  227.         tm->tm_hour += 12;
  228.     return 0;
  229. }
  230.  
  231.  
  232. /* return -1 on failure */
  233. int
  234. parsetime(time, tm)
  235. register char *time;
  236. register struct tm *tm;
  237. {
  238.     register char c;
  239.     register int x;
  240.  
  241.     tm->tm_sec = 0;
  242.     GOBBLE_NUM(time, c, x, &tm->tm_hour);
  243.     if (c != ':')
  244.         return -1;        /* only hour; too short */
  245.     GOBBLE_NUM(time, c, x, &tm->tm_min);
  246.     if (c != ':')
  247.         return 0;        /* no seconds; okay */
  248.     GOBBLE_NUM(time, c, x, &tm->tm_sec);
  249.     /* this may be considered too strict.  garbage at end of time? */
  250.     return (c == '\0' || ISSPACE(c)? 0: -1);
  251. }
  252.  
  253.  
  254. /*
  255.  * split - divide a string into fields, like awk split()
  256.  */
  257. int                /* number of fields, including overflow */
  258. split(string, fields, nfields, sep)
  259. char *string;
  260. char *fields[];            /* list is not NULL-terminated */
  261. int nfields;            /* number of entries available in fields[] */
  262. char *sep;            /* "" white, "c" single char, "ab" [ab]+ */
  263. {
  264.     register char *p = string;
  265.     register char c;            /* latest character */
  266.     register char sepc = sep[0];
  267.     register char sepc2;
  268.     register int fn;
  269.     register char **fp = fields;
  270.     register char *sepp;
  271.     register int trimtrail;
  272.  
  273.     /* white space */
  274.     if (sepc == '\0') {
  275.         while ((c = *p++) == ' ' || c == '\t')
  276.             continue;
  277.         p--;
  278.         trimtrail = 1;
  279.         sep = " \t";    /* note, code below knows this is 2 long */
  280.         sepc = ' ';
  281.     } else
  282.         trimtrail = 0;
  283.     sepc2 = sep[1];        /* now we can safely pick this up */
  284.  
  285.     /* catch empties */
  286.     if (*p == '\0')
  287.         return(0);
  288.  
  289.     /* single separator */
  290.     if (sepc2 == '\0') {
  291.         fn = nfields;
  292.         for (;;) {
  293.             *fp++ = p;
  294.             fn--;
  295.             if (fn == 0)
  296.                 break;
  297.             while ((c = *p++) != sepc)
  298.                 if (c == '\0')
  299.                     return(nfields - fn);
  300.             *(p-1) = '\0';
  301.         }
  302.         /* we have overflowed the fields vector -- just count them */
  303.         fn = nfields;
  304.         for (;;) {
  305.             while ((c = *p++) != sepc)
  306.                 if (c == '\0')
  307.                     return(fn);
  308.             fn++;
  309.         }
  310.         /* not reached */
  311.     }
  312.  
  313.     /* two separators */
  314.     if (sep[2] == '\0') {
  315.         fn = nfields;
  316.         for (;;) {
  317.             *fp++ = p;
  318.             fn--;
  319.             while ((c = *p++) != sepc && c != sepc2)
  320.                 if (c == '\0') {
  321.                     if (trimtrail && **(fp-1) == '\0')
  322.                         fn++;
  323.                     return(nfields - fn);
  324.                 }
  325.             if (fn == 0)
  326.                 break;
  327.             *(p-1) = '\0';
  328.             while ((c = *p++) == sepc || c == sepc2)
  329.                 continue;
  330.             p--;
  331.         }
  332.         /* we have overflowed the fields vector -- just count them */
  333.         fn = nfields;
  334.         while (c != '\0') {
  335.             while ((c = *p++) == sepc || c == sepc2)
  336.                 continue;
  337.             p--;
  338.             fn++;
  339.             while ((c = *p++) != '\0' && c != sepc && c != sepc2)
  340.                 continue;
  341.         }
  342.         /* might have to trim trailing white space */
  343.         if (trimtrail) {
  344.             p--;
  345.             while ((c = *--p) == sepc || c == sepc2)
  346.                 continue;
  347.             p++;
  348.             if (*p != '\0') {
  349.                 if (fn == nfields+1)
  350.                     *p = '\0';
  351.                 fn--;
  352.             }
  353.         }
  354.         return(fn);
  355.     }
  356.  
  357.     /* n separators */
  358.     fn = 0;
  359.     for (;;) {
  360.         if (fn < nfields)
  361.             *fp++ = p;
  362.         fn++;
  363.         for (;;) {
  364.             c = *p++;
  365.             if (c == '\0')
  366.                 return(fn);
  367.             sepp = sep;
  368.             while ((sepc = *sepp++) != '\0' && sepc != c)
  369.                 continue;
  370.             if (sepc != '\0')    /* it was a separator */
  371.                 break;
  372.         }
  373.         if (fn < nfields)
  374.             *(p-1) = '\0';
  375.         for (;;) {
  376.             c = *p++;
  377.             sepp = sep;
  378.             while ((sepc = *sepp++) != '\0' && sepc != c)
  379.                 continue;
  380.             if (sepc == '\0')    /* it wasn't a separator */
  381.                 break;
  382.         }
  383.         p--;
  384.     }
  385.  
  386.     /* not reached */
  387. }
  388.  
  389. /*
  390.  * Given an AbsoluteTime return the English text version of the date
  391.  *
  392.  * e.g. January 1 1970
  393.  */
  394. char *
  395. nabstimeout(time)
  396.     AbsoluteTime time;
  397. {
  398.     char *outStr;
  399.     char *tzoneStr;
  400.     struct tm timeVals;
  401.     struct tm *timeValp;
  402.     char month[16];
  403.     char weekday[16];
  404.  
  405.     outStr = (char *)palloc(64);
  406.  
  407.     if (time == EPOCH_ABSTIME)
  408.     {
  409.     strcpy(outStr, "epoch");
  410.     return outStr;
  411.     }
  412.     if (time == INVALID_ABSTIME)
  413.     {
  414.     strcpy(outStr, "Invalid Abstime");
  415.     return outStr;
  416.     }
  417.  
  418.     timeValp = localtime((time_t *)&time);
  419.     MonthNumToStr(timeValp->tm_mon, month);
  420.     WeekdayToStr(timeValp->tm_wday, weekday);
  421.  
  422.     /*
  423.      *  Dynix 3.0.17.10 and Ultrix WS 2 don't provide tm_zone.
  424.      */
  425.  
  426.     /*
  427.      * Linux has timezones in an external tzname[], not struct tm. (kai)
  428.      */
  429. #ifdef linux
  430.     tzoneStr = tzname[timeValp->tm_isdst];
  431. #else
  432. #ifdef sequent
  433.     tzoneStr = "";
  434. #else
  435. #ifdef OLD_DEC
  436.     tzoneStr = "";
  437. #else
  438.     tzoneStr = timeValp->tm_zone;
  439. #endif
  440. #endif
  441. #endif
  442.  
  443.     sprintf(outStr,
  444.         "%s %s %d %2.2d:%2.2d:%2.2d %d %s",
  445.         weekday,
  446.         month,
  447.         timeValp->tm_mday,
  448.         timeValp->tm_hour,
  449.         timeValp->tm_min,
  450.         timeValp->tm_sec,
  451.         (timeValp->tm_year >= 69) ? timeValp->tm_year+1900 :
  452.                     timeValp->tm_year+2000,
  453.         tzoneStr);
  454.     return(outStr);
  455. }
  456.  
  457. int
  458. MonthNumToStr(mnum, mstr)
  459.     int mnum;
  460.     char *mstr;
  461. {
  462.     switch(mnum)
  463.     {
  464.     case 0:
  465.         strcpy(mstr, "Jan");
  466.         break;
  467.     case 1:
  468.         strcpy(mstr, "Feb");
  469.         break;
  470.     case 2:
  471.         strcpy(mstr, "Mar");
  472.         break;
  473.     case 3:
  474.         strcpy(mstr, "Apr");
  475.         break;
  476.     case 4:
  477.         strcpy(mstr, "May");
  478.         break;
  479.     case 5:
  480.         strcpy(mstr, "Jun");
  481.         break;
  482.     case 6:
  483.         strcpy(mstr, "Jul");
  484.         break;
  485.     case 7:
  486.         strcpy(mstr, "Aug");
  487.         break;
  488.     case 8:
  489.         strcpy(mstr, "Sep");
  490.         break;
  491.     case 9:
  492.         strcpy(mstr, "Oct");
  493.         break;
  494.     case 10:
  495.         strcpy(mstr, "Nov");
  496.         break;
  497.     case 11:
  498.         strcpy(mstr, "Dec");
  499.         break;
  500.     default:
  501.         strcpy(mstr, "Bad Month");
  502.         break;
  503.     }
  504.     return 1;
  505. }
  506.  
  507. int
  508. WeekdayToStr(wday, wstr)
  509.     int wday;
  510.     char *wstr;
  511. {
  512.     switch(wday)
  513.     {
  514.     case 0:
  515.         strcpy(wstr, "Sun");
  516.         break;
  517.     case 1:
  518.         strcpy(wstr, "Mon");
  519.         break;
  520.     case 2:
  521.         strcpy(wstr, "Tues");
  522.         break;
  523.     case 3:
  524.         strcpy(wstr, "Wed");
  525.         break;
  526.     case 4:
  527.         strcpy(wstr, "Thurs");
  528.         break;
  529.     case 5:
  530.         strcpy(wstr, "Fri");
  531.         break;
  532.     case 6:
  533.         strcpy(wstr, "Sat");
  534.         break;
  535.     default:
  536.         strcpy(wstr, "Bad Weekday");
  537.         break;
  538.     }
  539.     return 1;
  540. }
  541.  
  542. /* turn a (struct tm) and a few variables into a time_t, with range checking */
  543. AbsoluteTime
  544. dateconv(tm, zone)
  545. register struct tm *tm;
  546. int zone;
  547. {
  548.     tm->tm_wday = tm->tm_yday = 0;
  549.  
  550.     /* validate, before going out of range on some members */
  551.     if (tm->tm_year < 0 || tm->tm_mon < 1 || tm->tm_mon > 12 ||
  552.         tm->tm_mday < 1 || tm->tm_hour < 0 || tm->tm_hour >= 24 ||
  553.         tm->tm_min < 0 || tm->tm_min > 59 ||
  554.         tm->tm_sec < 0 || tm->tm_sec > 59)
  555.         return -1;
  556.  
  557.     /*
  558.      * zone should really be -zone, and tz should be set to tp->value, not
  559.      * -tp->value.  Or the table could be fixed.
  560.      */
  561.     tm->tm_min += zone;        /* mktime lets it be out of range */
  562.  
  563.     /* convert to seconds */
  564.     return qmktime(tm);
  565. }
  566.  
  567.  
  568. /*
  569.  * near-ANSI qmktime suitable for use by dateconv; not necessarily as paranoid
  570.  * as ANSI requires, and it may not canonicalise the struct tm.  Ignores tm_wday
  571.  * and tm_yday.
  572.  */
  573. time_t
  574. qmktime(tp)
  575. register struct tm *tp;
  576. {
  577.     register int mon = tp->tm_mon;
  578.     register int day = tp->tm_mday, year = tp->tm_year;
  579.     register time_t daynum;
  580.     register int century;
  581.     time_t nrdaynum;
  582.  
  583.     /* If it was a 2 digit year */
  584.     if (year < 100)
  585.         year += 1900;
  586.     if (year < EPOCH)
  587.         return -1;        /* can't represent early date */
  588.  
  589.     /*
  590.      * validate day against days-per-month table, with leap-year
  591.      * correction
  592.      */
  593.     if (day > nmdays[mon])
  594.         if (mon != 2 || year % 4 == 0 &&
  595.             (year % 100 != 0 || year % 400 == 0) && day > 29)
  596.             return -1;    /* day too large for month */
  597.  
  598.     /* split year into century and year-of-century */
  599.     century = year / 100;
  600.     year %= 100;
  601.     /*
  602.      * We calculate the day number exactly, assuming the calendar has
  603.      * always had the current leap year rules.  (The leap year rules are
  604.      * to compensate for the fact that the Earth's revolution around the
  605.      * Sun takes 365.2425 days).  We first need to rotate months so March
  606.      * is 0, since we want the last month to have the reduced number of
  607.      * days.
  608.      */
  609.     if (mon > 2)
  610.         mon -= 3;
  611.     else {
  612.         mon += 9;
  613.         if (year == 0) {
  614.             century--;
  615.             year = 99;
  616.         } else
  617.             --year;
  618.     }
  619.     daynum = -EPOCH_DAYNUM + DAYNUM(century, year, mon, day);
  620.  
  621.     /* convert to seconds */
  622.     nrdaynum = daynum =
  623.         tp->tm_sec + (tp->tm_min +(daynum*24 + tp->tm_hour)*60)*60;
  624.  
  625.     /* daylight correction */
  626.     if (tp->tm_isdst < 0)        /* unknown; find out */
  627.         tp->tm_isdst = localtime(&nrdaynum)->tm_isdst;
  628.     if (tp->tm_isdst > 0)
  629.         daynum -= 60*60;
  630.  
  631.     return daynum < 0? -1: daynum;
  632. }
  633.  
  634. /* Globals */
  635. static int dtok_numparsed;
  636.  
  637. /* forwards */
  638. extern datetkn datetktbl[];
  639. extern unsigned szdatetktbl;
  640.  
  641. datetkn *
  642. datetoktype(s, bigvalp)
  643. char *s;
  644. int *bigvalp;
  645. {
  646.     register char *cp = s;
  647.     register char c = *cp;
  648.     static datetkn t;
  649.     register datetkn *tp = &t;
  650.  
  651.     if (isascii(c) && isdigit(c)) {
  652.         register int len = strlen(cp);
  653.  
  654.         if (len > 3 && (cp[1] == ':' || cp[2] == ':'))
  655.             tp->type = TIME;
  656.         else {
  657.             if (bigvalp != NULL)
  658.                 /* won't fit in tp->value */
  659.                 *bigvalp = atoi(cp);
  660.             if (len == 4)
  661.                 tp->type = YEAR;
  662.             else if (++dtok_numparsed == 1)
  663.                 tp->type = DAY;
  664.             else
  665.                 tp->type = YEAR;
  666.         }
  667.     } else if (c == '-' || c == '+') {
  668.         register int val = atoi(cp + 1);
  669.         register int hr =  val / 100;
  670.         register int min = val % 100;
  671.  
  672.         val = hr*60 + min;
  673.         if (c == '-')
  674.             val = -val;
  675.         tp->type = TZ;
  676.         TOVAL(tp, val);
  677.     } else {
  678.         char lowtoken[TOKMAXLEN+1];
  679.         register char *ltp = lowtoken, *endltp = lowtoken+TOKMAXLEN;
  680.  
  681.         /* copy to lowtoken to avoid modifying s */
  682.         while ((c = *cp++) != '\0' && ltp < endltp)
  683.             *ltp++ = (isascii(c) && isupper(c)? tolower(c): c);
  684.         *ltp = '\0';
  685.         tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
  686.         if (tp == NULL) {
  687.             tp = &t;
  688.             tp->type = IGNORE;
  689.         }
  690.     }
  691.     return tp;
  692. }
  693.  
  694. /*
  695.  * Binary search -- from Knuth (6.2.1) Algorithm B.  Special case like this
  696.  * is WAY faster than the generic bsearch().
  697.  */
  698. datetkn *
  699. datebsearch(key, base, nel)
  700. register char *key;
  701. register datetkn *base;
  702. unsigned int nel;
  703. {
  704.     register datetkn *last = base + nel - 1, *position;
  705.     register int result;
  706.  
  707.     while (last >= base) {
  708.         position = base + ((last - base) >> 1);
  709.         result = key[0] - position->token[0];
  710.         if (result == 0) {
  711.             result = strncmp(key, position->token, TOKMAXLEN);
  712.             if (result == 0)
  713.                 return position;
  714.         }
  715.         if (result < 0)
  716.             last = position - 1;
  717.         else
  718.             base = position + 1;
  719.     }
  720.     return 0;
  721. }
  722.  
  723.  
  724. /*
  725.  * to keep this table reasonably small, we divide the lexval for TZ and DTZ
  726.  * entries by 10 and truncate the text field at MAXTOKLEN characters.
  727.  * the text field is not guaranteed to be NUL-terminated.
  728.  */
  729. static datetkn datetktbl[] = {
  730. /*    text        token    lexval */
  731.     "acsst",    DTZ,    63,        /* Cent. Australia */
  732.     "acst",        TZ,    57,        /* Cent. Australia */
  733.     "adt",        DTZ,    NEG(18),    /* Atlantic Daylight Time */
  734.     "aesst",    DTZ,    66,        /* E. Australia */
  735.     "aest",        TZ,    60,        /* Australia Eastern Std Time */
  736.     "ahst",        TZ,    60,        /* Alaska-Hawaii Std Time */
  737.     "am",        AMPM,    AM,
  738.     "apr",        MONTH,    4,
  739.     "april",    MONTH,    4,
  740.     "ast",        TZ,    NEG(24),    /* Atlantic Std Time (Canada) */
  741.     "at",        IGNORE,    0,        /* "at" (throwaway) */
  742.     "aug",        MONTH,    8,
  743.     "august",    MONTH,    8,
  744.     "awsst",    DTZ,    54,        /* W. Australia */
  745.     "awst",        TZ,    48,        /* W. Australia */
  746.     "bst",        TZ,    6,        /* British Summer Time */
  747.     "bt",        TZ,    18,        /* Baghdad Time */
  748.     "cadt",        DTZ,    63,        /* Central Australian DST */
  749.     "cast",        TZ,    57,        /* Central Australian ST */
  750.     "cat",        TZ,    NEG(60),    /* Central Alaska Time */
  751.     "cct",        TZ,    48,        /* China Coast */
  752.     "cdt",        DTZ,    NEG(30),    /* Central Daylight Time */
  753.     "cet",        TZ,    6,        /* Central European Time */
  754.     "cetdst",    DTZ,    12,        /* Central European Dayl.Time */
  755.     "cst",        TZ,    NEG(36),    /* Central Standard Time */
  756.     "dec",        MONTH,    12,
  757.     "decemb",    MONTH,    12,
  758.     "dnt",        TZ,    6,        /* Dansk Normal Tid */
  759.     "dst",        IGNORE,    0,
  760.     "east",        TZ,    NEG(60),    /* East Australian Std Time */
  761.     "edt",        DTZ,    NEG(24),    /* Eastern Daylight Time */
  762.     "eet",        TZ,    12,        /* East. Europe, USSR Zone 1 */
  763.     "eetdst",    DTZ,    18,        /* Eastern Europe */
  764.     "est",        TZ,    NEG(30),    /* Eastern Standard Time */
  765.     "feb",        MONTH,    2,
  766.     "februa",    MONTH,    2,
  767.     "fri",        IGNORE,    5,
  768.     "friday",    IGNORE,    5,
  769.     "fst",        TZ,    6,        /* French Summer Time */
  770.     "fwt",        DTZ,    12,        /* French Winter Time  */
  771.     "gmt",        TZ,    0,        /* Greenwish Mean Time */
  772.     "gst",        TZ,    60,        /* Guam Std Time, USSR Zone 9 */
  773.     "hdt",        DTZ,    NEG(54),    /* Hawaii/Alaska */
  774.     "hmt",        DTZ,    18,        /* Hellas ? ? */
  775.     "hst",        TZ,    NEG(60),    /* Hawaii Std Time */
  776.     "idle",        TZ,    72,        /* Intl. Date Line, East */
  777.     "idlw",        TZ,    NEG(72),    /* Intl. Date Line, West */
  778.     "ist",        TZ,    12,        /* Israel */
  779.     "it",        TZ,    22,        /* Iran Time */
  780.     "jan",        MONTH,    1,
  781.     "januar",    MONTH,    1,
  782.     "jst",        TZ,    54,        /* Japan Std Time,USSR Zone 8 */
  783.     "jt",        TZ,    45,        /* Java Time */
  784.     "jul",        MONTH,    7,
  785.     "july",        MONTH,    7,
  786.     "jun",        MONTH,    6,
  787.     "june",        MONTH,    6,
  788.     "kst",        TZ,    54,        /* Korea Standard Time */
  789.     "ligt",        TZ,    60,        /* From Melbourne, Australia */
  790.     "mar",        MONTH,    3,
  791.     "march",    MONTH,    3,
  792.     "may",        MONTH,    5,
  793.     "mdt",        DTZ,    NEG(36),    /* Mountain Daylight Time */
  794.     "mest",        DTZ,    12,        /* Middle Europe Summer Time */
  795.     "met",        TZ,    6,        /* Middle Europe Time */
  796.     "metdst",    DTZ,    12,        /* Middle Europe Daylight Time*/
  797.     "mewt",        TZ,    6,        /* Middle Europe Winter Time */
  798.     "mez",        TZ,    6,        /* Middle Europe Zone */
  799.     "mon",        IGNORE,    1,
  800.     "monday",    IGNORE,    1,
  801.     "mst",        TZ,    NEG(42),    /* Mountain Standard Time */
  802.     "mt",        TZ,    51,        /* Moluccas Time */
  803.     "ndt",        DTZ,    NEG(15),    /* Nfld. Daylight Time */
  804.     "nft",        TZ,    NEG(21),    /* Newfoundland Standard Time */
  805.     "nor",        TZ,    6,        /* Norway Standard Time */
  806.     "nov",        MONTH,    11,
  807.     "novemb",    MONTH,    11,
  808.     "nst",        TZ,    NEG(21),    /* Nfld. Standard Time */
  809.     "nt",        TZ,    NEG(66),    /* Nome Time */
  810.     "nzdt",        DTZ,    78,        /* New Zealand Daylight Time */
  811.     "nzst",        TZ,    72,        /* New Zealand Standard Time */
  812.     "nzt",        TZ,    72,        /* New Zealand Time */
  813.     "oct",        MONTH,    10,
  814.     "octobe",    MONTH,    10,
  815.     "on",        IGNORE,    0,        /* "on" (throwaway) */
  816.     "pdt",        DTZ,    NEG(42),    /* Pacific Daylight Time */
  817.     "pm",        AMPM,    PM,
  818.     "pst",        TZ,    NEG(48),    /* Pacific Standard Time */
  819.     "sadt",        DTZ,    63,        /* S. Australian Dayl. Time */
  820.     "sast",        TZ,    57,        /* South Australian Std Time */
  821.     "sat",        IGNORE,    6,
  822.     "saturd",    IGNORE,    6,
  823.     "sep",        MONTH,    9,
  824.     "sept",        MONTH,    9,
  825.     "septem",    MONTH,    9,
  826.     "set",        TZ,    NEG(6),        /* Seychelles Time ?? */
  827.     "sst",        DTZ,    12,        /* Swedish Summer Time */
  828.     "sun",        IGNORE,    0,
  829.     "sunday",    IGNORE,    0,
  830.     "swt",        TZ,    6,        /* Swedish Winter Time  */
  831.     "thu",        IGNORE,    4,
  832.     "thur",        IGNORE,    4,
  833.     "thurs",    IGNORE,    4,
  834.     "thursd",    IGNORE,    4,
  835.     "tue",        IGNORE,    2,
  836.     "tues",        IGNORE,    2,
  837.     "tuesda",    IGNORE,    2,
  838.     "ut",        TZ,    0,
  839.     "utc",        TZ,    0,
  840.     "wadt",        DTZ,    48,        /* West Australian DST */
  841.     "wast",        TZ,    42,        /* West Australian Std Time */
  842.     "wat",        TZ,    NEG(6),        /* West Africa Time */
  843.     "wdt",        DTZ,    54,        /* West Australian DST */
  844.     "wed",        IGNORE,    3,
  845.     "wednes",    IGNORE,    3,
  846.     "weds",        IGNORE,    3,
  847.     "wet",        TZ,    0,        /* Western Europe */
  848.     "wetdst",    DTZ,    6,        /* Western Europe */
  849.     "wst",        TZ,    48,        /* West Australian Std Time */
  850.     "ydt",        DTZ,    NEG(48),    /* Yukon Daylight Time */
  851.     "yst",        TZ,    NEG(54),    /* Yukon Standard Time */
  852.     "zp4",        TZ,    NEG(24),    /* GMT +4  hours. */
  853.     "zp5",        TZ,    NEG(30),    /* GMT +5  hours. */
  854.     "zp6",        TZ,    NEG(36),    /* GMT +6  hours. */
  855. };
  856.  
  857. #if    0
  858. /*
  859.  * these time zones are orphans, i.e. the name is also used by a more
  860.  * likely-to-appear time zone
  861.  */
  862.     "at",        TZ,    NEG(12),    /* Azores Time */
  863.     "bst",        TZ,    NEG(18),    /* Brazil Std Time */
  864.     "bt",        TZ,    NEG(66),    /* Bering Time */
  865.     "edt",        TZ,    66,        /* Australian Eastern DaylTime*/
  866.     "est",        TZ,    60,        /* Australian Eastern Std Time*/
  867.     "ist",        TZ,    33,        /* Indian Standard Time */
  868.     "nst",        TZ,    51,        /* North Sumatra Time */
  869.     "sst",        TZ,    42,        /* South Sumatra, USSR Zone 6 */
  870.     "sst",        TZ,    48,        /* Singapore Std Time */
  871.     "wet",        TZ,    6,        /* Western European Time */
  872. /* military timezones are deprecated by RFC 1123 section 5.2.14 */
  873.     "a",        TZ,    6,        /* UTC+1h */
  874.     "b",        TZ,    12,        /* UTC+2h */
  875.     "c",        TZ,    18,        /* UTC+3h */
  876.     "d",        TZ,    24,        /* UTC+4h */
  877.     "e",        TZ,    30,        /* UTC+5h */
  878.     "f",        TZ,    36,        /* UTC+6h */
  879.     "g",        TZ,    42,        /* UTC+7h */
  880.     "h",        TZ,    48,        /* UTC+8h */
  881.     "i",        TZ,    54,        /* UTC+9h */
  882.     "k",        TZ,    60,        /* UTC+10h */
  883.     "l",        TZ,    66,        /* UTC+11h */
  884.     "m",        TZ,    72,        /* UTC+12h */
  885.     "n",        TZ,    NEG(6),        /* UTC-1h */
  886.     "o",        TZ,    NEG(12),    /* UTC-2h */
  887.     "p",        TZ,    NEG(18),    /* UTC-3h */
  888.     "q",        TZ,    NEG(24),    /* UTC-4h */
  889.     "r",        TZ,    NEG(30),    /* UTC-5h */
  890.     "s",        TZ,    NEG(36),    /* UTC-6h */
  891.     "t",        TZ,    NEG(42),    /* UTC-7h */
  892.     "u",        TZ,    NEG(48),    /* UTC-8h */
  893.     "v",        TZ,    NEG(54),    /* UTC-9h */
  894.     "w",        TZ,    NEG(60),    /* UTC-10h */
  895.     "x",        TZ,    NEG(66),    /* UTC-11h */
  896.     "y",        TZ,    NEG(72),    /* UTC-12h */
  897.     "z",        TZ,    0,        /* UTC */
  898. #endif
  899.  
  900. static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
  901.  
  902.  
  903. /*
  904.  *  AbsoluteTimeIsBefore -- true iff time1 is before time2.
  905.  *
  906.  *    Since we store AbsoluteTimes as unsigned quantities, the comparison
  907.  *    below would fail for times before the Unix epoch.  We cast them to
  908.  *    signed quantities for the sake of this comparison, even though it
  909.  *    violates the type-hiding abstraction.
  910.  *
  911.  *    The above comment isn't really valid anymore since the change
  912.  *    to absolute time storage. -mer 15 Feb. 1992
  913.  */
  914.  
  915. bool
  916. AbsoluteTimeIsBefore(time1, time2)
  917.     AbsoluteTime    time1;
  918.     AbsoluteTime    time2;
  919. {
  920.     Assert(AbsoluteTimeIsValid(time1));
  921.     Assert(AbsoluteTimeIsValid(time2));
  922.  
  923.     if ((time1 == time2) || (time2 == EPOCH_ABSTIME))
  924.     return false;
  925.     if (time1 == EPOCH_ABSTIME)
  926.     return true;
  927.  
  928.     return ((bool)((int32) time1 < (int32) time2));
  929. }
  930.  
  931. bool
  932. AbsoluteTimeIsAfter(time1, time2)
  933.     AbsoluteTime    time1;
  934.     AbsoluteTime    time2;
  935. {
  936.     Assert(AbsoluteTimeIsValid(time1));
  937.     Assert(AbsoluteTimeIsValid(time2));
  938.  
  939.     if ((time1 == time2) || (time1 == EPOCH_ABSTIME))
  940.     return false;
  941.     if (time2 == EPOCH_ABSTIME)
  942.     return true;
  943.  
  944.     return ((bool) ((int32) time1 > (int32) time2));
  945. }
  946.  
  947.  
  948. /*
  949.  *    abstimeeq    - returns 1, iff arguments are equal
  950.  *    abstimene    - returns 1, iff arguments are not equal
  951.  *    abstimelt    - returns 1, iff t1 less than t2
  952.  *    abstimegt    - returns 1, iff t1 greater than t2
  953.  *    abstimele    - returns 1, iff t1 less than or equal to t2
  954.  *    abstimege    - returns 1, iff t1 greater than or equal to t2
  955.  *
  956.  */
  957. int32
  958. abstimeeq(t1,t2)
  959.     AbsoluteTime    t1,t2;
  960. { return(t1 == t2); }
  961.  
  962. int32
  963. abstimene(t1, t2)
  964.     AbsoluteTime    t1,t2;
  965. { return(t1 != t2); }
  966.  
  967. int32
  968. abstimelt(t1, t2)
  969.     int32 /* AbsoluteTime */    t1,t2;
  970. { return(t1 < t2); }
  971.  
  972. int32
  973. abstimegt(t1, t2)
  974.     int32 /* AbsoluteTime */    t1,t2;
  975. { return(t1 > t2); }
  976.  
  977. int32
  978. abstimele(t1, t2)
  979.     int32 /* AbsoluteTime */    t1,t2;
  980. { return(t1 <= t2); }
  981.  
  982. int32
  983. abstimege(t1, t2)
  984.     int32 /* AbsoluteTime */    t1,t2;
  985. { return(t1 >= t2); }
  986.  
  987.  
  988. /*
  989.  *    isabstime    - returns 1, iff datestring is of type abstime
  990.  *                (and returns the convertion in brokentime)
  991.  *              returns 0 for any syntax error
  992.  *              returns 2 for time 'now'
  993.  *
  994.  */
  995. int
  996. isabstime(datestring, brokentime)
  997.     char    *datestring;
  998.     struct tm    *brokentime;
  999. {
  1000.     int tz = 0;
  1001.     struct tm dummy;
  1002.  
  1003.     if (brokentime == NULL)
  1004.     brokentime = &dummy;
  1005.  
  1006.     if (IsNowStr(datestring))
  1007.     return 2;
  1008.     if (prsabsdate(datestring, NULL, brokentime, &tz) >= 0)
  1009.     return 1;
  1010.     return 0;
  1011. }
  1012.